-- C940A03.A
--
--                             Grant of Unlimited Rights
--
--     Under contracts F33600-87-D-0337, F33600-84-D-0280, MDA903-79-C-0687,
--     F08630-91-C-0015, and DCA100-97-D-0025, the U.S. Government obtained 
--     unlimited rights in the software and documentation contained herein.
--     Unlimited rights are defined in DFAR 252.227-7013(a)(19).  By making 
--     this public release, the Government intends to confer upon all 
--     recipients unlimited rights  equal to those held by the Government.  
--     These rights include rights to use, duplicate, release or disclose the 
--     released technical data and computer software in whole or in part, in 
--     any manner and for any purpose whatsoever, and to have or permit others 
--     to do so.
--
--                                    DISCLAIMER
--
--     ALL MATERIALS OR INFORMATION HEREIN RELEASED, MADE AVAILABLE OR
--     DISCLOSED ARE AS IS.  THE GOVERNMENT MAKES NO EXPRESS OR IMPLIED 
--     WARRANTY AS TO ANY MATTER WHATSOEVER, INCLUDING THE CONDITIONS OF THE
--     SOFTWARE, DOCUMENTATION OR OTHER INFORMATION RELEASED, MADE AVAILABLE 
--     OR DISCLOSED, OR THE OWNERSHIP, MERCHANTABILITY, OR FITNESS FOR A
--     PARTICULAR PURPOSE OF SAID MATERIAL.
--*
--
-- OBJECTIVE:
--      Check that a protected object provides coordinated access to
--      shared data.  Check that it can implement a semaphore-like construct
--      controlling access to shared data through procedure parameters to
--      allow a specific maximum number of tasks to run and exclude all
--      others.
--
-- TEST DESCRIPTION:
--      Declare a resource descriptor tagged type.  Extend the type and
--      use the extended type in a protected data structure.
--      Implement a counting semaphore type that can be initialized to a 
--      specific number of available resources.  Declare an entry for 
--      requesting a specific resource and an procedure for releasing the 
--      same resource it.  Declare an object of this (protected) type, 
--      initialized to two resources.  Declare and start three tasks each 
--      of which asks for a resource.  Verify that only two resources are 
--      granted and that the last task in is queued.
--
--      This test models a multi-user operating system that allows a limited
--      number of logins.  Users requesting login are modeled by tasks.
--
--
-- TEST FILES:
--      This test depends on the following foundation code:
--
--         F940A00
--
--
-- CHANGE HISTORY:
--      06 Dec 94   SAIC    ACVC 2.0
--      13 Nov 95   SAIC    Fixed bugs for ACVC 2.0.1
--
--!

package C940A03_0 is
      --Resource_Pkg

  -- General type declarations that will be extended to model available
  -- logins

  type Resource_ID_Type is range 0..10;
  type Resource_Type is tagged record 
    Id : Resource_ID_Type := 0;
  end record;

end C940A03_0;
  --Resource_Pkg

--======================================--
-- no body for C940A3_0
--======================================--

with F940A00;     -- Interlock_Foundation
with C940A03_0;   -- Resource_Pkg;

package C940A03_1 is
     -- Semaphores

  -- Models a counting semaphore that will allow up to a specific 
  -- number of logins
  -- Users (tasks) request a login slot by calling the Request_Login
  -- entry and logout by calling the Release_Login procedure

  Max_Logins : constant Integer := 2;
  

  type Key_Type is range 0..100;
                                      -- When a user requests a login, an
                                      -- identifying key will be returned
  Init_Key : constant Key_Type := 0;

  type Login_Record_Type is new C940A03_0.Resource_Type with record
    Key  : Key_Type := Init_Key;
  end record;


  protected type Login_Semaphore_Type (Resources_Available : Integer :=1) is

    entry     Request_Login   (Resource_Key : in out Login_Record_Type);
    procedure Release_Login;
    function  Available return Integer;  -- how many logins are available?
  private
    Logins_Avail : Integer  := Resources_Available;
    Next_Key     : Key_Type := Init_Key;

  end Login_Semaphore_Type;

  Login_Semaphore      : Login_Semaphore_Type (Max_Logins);

  --====== machinery for the test, not the model =====--
  TC_Control_Message : F940A00.Interlock_Type;    
  function TC_Key_Val (Login_Rec : Login_Record_Type) return Integer;


end C940A03_1;
 -- Semaphores;

--=========================================================--

package body C940A03_1 is
          -- Semaphores is

  protected body Login_Semaphore_Type is

    entry Request_Login (Resource_Key : in out Login_Record_Type)
                                             when Logins_Avail > 0 is
    begin
      Next_Key := Next_Key + 1;        -- login process returns a key
      Resource_Key.Key := Next_Key;     -- to the requesting user
      Logins_Avail := Logins_Avail - 1;
    end Request_Login;

    procedure Release_Login is
    begin
      Logins_Avail := Logins_Avail + 1;
    end Release_Login;

    function Available return Integer is
      begin
        return Logins_Avail;
      end Available;

  end Login_Semaphore_Type;

  function TC_Key_Val (Login_Rec : Login_Record_Type) return Integer is
    begin
      return Integer (Login_Rec.Key);
    end TC_Key_Val;

end C940A03_1;
 -- Semaphores;

--=========================================================--

with C940A03_0;    -- Resource_Pkg,
with C940A03_1;    -- Semaphores;

package C940A03_2 is
     -- Task_Pkg

  package Semaphores renames C940A03_1;

  task type User_Task_Type is

     entry Login  (user_id : C940A03_0.Resource_Id_Type);
                      -- instructs the task to ask for a login
     entry Logout;    -- instructs the task to release the login
     --=======================--
     -- this entry is used to get information to verify test operation
     entry Get_Status (User_Record : out Semaphores.Login_Record_Type);

  end User_Task_Type;

end C940A03_2;
 -- Task_Pkg

--=========================================================--

with Report;
with C940A03_0;   -- Resource_Pkg,
with C940A03_1;   -- Semaphores,
with F940A00;     -- Interlock_Foundation;
    
package body C940A03_2 is
          -- Task_Pkg 

  -- This task models a user requesting a login from the system
  -- For control of this test, we can ask the task to login, logout, or
  -- give us the current user record (containing login information)

  task body User_Task_Type is
    Rec  : Semaphores.Login_Record_Type;
  begin
    loop
      select
        accept Login (user_id : C940A03_0.Resource_Id_Type) do
          Rec.Id := user_id;
        end Login;

        Semaphores.Login_Semaphore.Request_Login (Rec);
                      -- request a resource; if resource is not available, 
                      -- task will be queued to wait

        --== following is test control machinery ==--
        F940A00.Counter.Increment;
        Semaphores.TC_Control_Message.Post;   
               -- after resource is obtained, post message

      or
        accept Logout do  
          Semaphores.Login_Semaphore.Release_Login;
                      -- release the resource
          --== test control machinery ==--
          F940A00.Counter.Decrement;
        end Logout;
        exit;

      or
        accept Get_Status (User_Record : out Semaphores.Login_Record_Type) do
          User_Record := Rec;
        end Get_Status;

      end select;  
    end loop;

  exception
    when others => Report.Failed ("Exception raised in model user task");
  end User_Task_Type;

end C940A03_2;
 -- Task_Pkg

--=========================================================--

with Report;
with ImpDef;
with C940A03_1;   -- Semaphores, 
with C940A03_2;   -- Task_Pkg,
with F940A00;     -- Interlock_Foundation;

procedure C940A03 is

  package Semaphores renames C940A03_1;
  package Users      renames C940A03_2;  

  Task1, Task2, Task3 : Users.User_Task_Type;
  User_Rec            : Semaphores.Login_Record_Type;

begin      -- Tasks start here

  Report.Test ("C940A03", "Check that a protected object can coordinate " &
                          "shared data access using procedure parameters");

  if F940A00.Counter.Number /=0 then
    Report.Failed ("Wrong initial conditions");
  end if;
                                 
  Task1.Login (1);               -- request resource; request should be granted
  Semaphores.TC_Control_Message.Consume;  
                                 -- ensure that task obtains resource by
                                 -- waiting for task to post message

                                 -- Task 1 waiting for call to Logout
                                 -- Others still available
  Task1.Get_Status (User_Rec);
  if (F940A00.Counter.Number /= 1)
  or (Semaphores.Login_Semaphore.Available /=1)
  or (Semaphores.TC_Key_Val (User_Rec) /= 1) then
    Report.Failed ("Resource not assigned to task 1");
  end if;
 
  Task2.Login (2);               -- Request for resource should be granted  
  Semaphores.TC_Control_Message.Consume;  
                                 -- ensure that task obtains resource by
                                 -- waiting for task to post message
 
  Task2.Get_Status (User_Rec);
  if (F940A00.Counter.Number /= 2)
  or (Semaphores.Login_Semaphore.Available /=0)
  or (Semaphores.TC_Key_Val (User_Rec) /= 2) then
    Report.Failed ("Resource not assigned to task 2");
  end if;
 

  Task3.Login (3);               -- request for resource should be denied
                                 -- and task queued


                                 -- Tasks 1 and 2 holds resources
                                 -- and are waiting for a call to Logout
                                 -- Task 3 is queued

  if (F940A00.Counter.Number /= 2)
  or (Semaphores.Login_Semaphore.Available /=0) then
    Report.Failed ("Resource incorrectly assigned to task 3");
  end if;

  Task1.Logout;                  -- released resource should be given to
                                 -- queued task
  Semaphores.TC_Control_Message.Consume;  
                                 -- wait for confirming message from task

                                 -- Task 1 holds no resources
                                 --   and is terminated (or will soon)
                                 -- Tasks 2 and 3 hold resources
                                 --   and are waiting for a call to Logout

  Task3.Get_Status (User_Rec);
  if (F940A00.Counter.Number /= 2)
                      or (Semaphores.Login_Semaphore.Available /=0)
                      or (Semaphores.TC_Key_Val (User_Rec) /= 3) then
    Report.Failed ("Resource not properly released/assigned to task 3");
  end if;

  Task2.Logout;                  -- no outstanding request for released
                                 -- resource
                                 -- Tasks 1 and 2 hold no resources
                                 -- Task 3 holds a resource
                                 --   and is waiting for a call to Logout

  if (F940A00.Counter.Number /= 1)
                        or (Semaphores.Login_Semaphore.Available /=1) then
    Report.Failed ("Resource not properly released from task 2");
  end if;

  Task3.Logout;

                                 -- all resources have been returned
                                 -- all tasks have terminated or will soon

  if (F940A00.Counter.Number /=0)
                          or (Semaphores.Login_Semaphore.Available /=2) then
    Report.Failed ("Resource not properly released from task 3");
  end if;

  -- Ensure all tasks have terminated before calling Result
  while not (Task1'terminated and     
             Task2'terminated and
             Task3'terminated)     loop
    delay ImpDef.Minimum_Task_Switch;   
  end loop;

  Report.Result;

end C940A03;
